package cn.com.twoke.game.client.games.tic_tac_toe;

import cn.com.twoke.game.client.games.framework.core.AcceptChatMessageAble;
import cn.com.twoke.game.client.games.framework.core.Game;
import cn.com.twoke.game.client.games.framework.core.GamePanel;
import cn.com.twoke.game.client.games.framework.ui.ChatMessage;
import cn.com.twoke.game.client.games.framework.ui.MessageListJPanel;
import cn.com.twoke.game.client.games.gomoku.entity.Chess;
import cn.com.twoke.game.client.games.tic_tac_toe.constants.TicTacToeChess;
import cn.com.twoke.game.client.games.tic_tac_toe.constants.TicTacToeGameConstant;
import cn.com.twoke.game.client.games.tic_tac_toe.controller.ChessController;
import cn.com.twoke.game.client.games.tic_tac_toe.controller.RobotChessController;
import cn.com.twoke.game.client.games.tic_tac_toe.entity.TicTacToeChessImpl;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Stack;

public class TicTacToeGame extends Game implements TicTacToeGameConstant
        , MouseListener, MouseMotionListener, AcceptChatMessageAble {

    public static final String ID = "tic-tac-toe";
    public static final String TITLE = "井字棋";
    private boolean canDropChess;
    private TicTacToeChessImpl droppingChess;
    private ChessController controller;



    private MessageListJPanel messageListJPanel;
    private JScrollPane scrollPane;

    private List<ChatMessage> messages;

    public TicTacToeGame(ChessController controller) {
        super(TicTacToeGame::buildGamePanel);
        this.panel.addMouseMotionListener(this);
        this.panel.addMouseListener(this);
        this.controller = controller;
        this.controller.setGame(this);
    }

    private static GamePanel buildGamePanel(Game game) {
        return new GamePanel(game, WINDOW_WIDTH, WINDOW_HEIGHT);
    }


    @Override
    protected String getTitle() {
        return withUserName(TITLE);
    }

    @Override
    protected void withComponent(JFrame frame) {
        this.canDropChess = true;
        this.droppingChess = new TicTacToeChessImpl(-1, -1);
        this.droppingChess.setType(TicTacToeChess.Type.CIRCLE);

        frame.add(panel, BorderLayout.CENTER);
        JPanel optionPanel = new JPanel();
        optionPanel.setPreferredSize(new Dimension(280,  WINDOW_HEIGHT));
        this.messages = new ArrayList<>();
        messageListJPanel = new MessageListJPanel(this, this.messages);
        scrollPane = new JScrollPane(messageListJPanel);
        final JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
        verticalScrollBar.setValue(verticalScrollBar.getMaximum());
        scrollPane.setPreferredSize(new Dimension(260,  450));
        // 设置滚动条的显示策略，确保在需要时显示滚动条
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        optionPanel.add(scrollPane, BorderLayout.NORTH);

        JPanel inputPanel = new JPanel();
        inputPanel.setLayout(new GridLayout(3, 1,0,10));
        /**
         * 发送消息
         */
        JButton sendMessageBtn = new JButton("发消息");
        sendMessageBtn.setPreferredSize(new Dimension(260, 38));
        sendMessageBtn.addActionListener(e -> {
            String content = JOptionPane.showInputDialog(this.getWindow(),"请输入发送的消息");
            if (null == content || "".equals(content)) {
                return;
            }
            controller.sendMessage(content);
        });
        inputPanel.add(sendMessageBtn);


        /**
         * 悔棋
         */
        JButton undoBtn = new JButton("悔棋");
        undoBtn.setPreferredSize(new Dimension(260, 38));
        inputPanel.add(undoBtn);
        undoBtn.addActionListener(e -> {
            if (this.chessNotes.isEmpty()) {
                JOptionPane.showMessageDialog(this.getWindow(), "没有可悔棋了");
                return;
            }
            controller.undo();
        });

        /**
         * 结束游戏
         */
        JButton gameOverBtn = new JButton("结束游戏");
        gameOverBtn.setPreferredSize(new Dimension(260, 38));
        gameOverBtn.addActionListener(e -> {
            controller.gameOver();
        });
        inputPanel.add(gameOverBtn);

        optionPanel.add(inputPanel);

        frame.add(optionPanel, BorderLayout.EAST);
        frame.pack();
    }

    @Override
    public String getId() {
        return ID;


    }

    @Override
    public void update(double deltaU) {

    }

    private static Rectangle2D GRID_PANEL = new Rectangle2D.Double(
            START_X, START_Y,
            CELL_WIDTH * COL, CELL_WIDTH * ROW
    );


    @Override
    public void render(Graphics2D g) {
        renderGrid(g);
        renderDroppedChess(g);
        renderDroppingChess(g);
    }

    private TicTacToeChessImpl[][] grid;
    private Stack<TicTacToeChessImpl> chessNotes;

    public TicTacToeChess.Type getUserDropingChessType() {
        return droppingChess.getType();
    }

    public TicTacToeChess.Type getNotUserDropingChessType() {
        return droppingChess.getType() == TicTacToeChess.Type.CIRCLE ? TicTacToeChess.Type.CROSS : TicTacToeChess.Type.CIRCLE;
    }


    @Override
    public void initGameState() {
        if (this.isRoomMaster) {
            droppingChess.setType(TicTacToeChess.Type.CIRCLE);
        } else {
            droppingChess.setType(TicTacToeChess.Type.CROSS);
            this.canDropChess = false;
        }
    }

    @Override
    protected void initialzer() {
        this.canDropChess = true;
        this.droppingChess = new TicTacToeChessImpl(-1, -1);
        this.droppingChess.setType(TicTacToeChess.Type.CIRCLE);
        // 初始化棋盘
        grid = new TicTacToeChessImpl[ROW][COL];
        for (int row = 0; row < ROW; row++) {
            for (int col = 0; col < COL; col++) {
                grid[row][col] = new TicTacToeChessImpl(col, row);
                this.add(grid[row][col]);
            }
        }
        // 操作记录
        chessNotes = new Stack<>();
    }

    private void renderDroppingChess(Graphics2D g) {
        droppingChess.render(g);
    }


    private void renderDroppedChess(Graphics2D g) {
        super.render(g);
    }


    private void renderGrid(Graphics2D g) {
        g.setColor(GRID_BG_COLOR);
        g.fill(GRID_PANEL);
        for (int i = 1; i < COL; i++) {
            g.setColor(GRID_LINE_COLOR);
            g.drawLine( START_X + i * CELL_WIDTH, START_Y + 0, START_X + i * CELL_WIDTH, START_Y + CELL_WIDTH * ROW);
        }
        for (int j = 1; j < ROW; j++) {
            g.setColor(GRID_LINE_COLOR);
            g.drawLine(START_X + 0, START_Y + j * CELL_WIDTH, START_X +  CELL_WIDTH * COL, START_Y + j * CELL_WIDTH);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (canDropChess && droppingChess.getHitbox().getBounds2D().contains(e.getX(), e.getY())) {
            droppingChess.hide();
            controller.drop(droppingChess.getPosition().x,
                    droppingChess.getPosition().y);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    @Override
    public void mouseDragged(MouseEvent e) {

    }

    @Override
    public void mouseMoved(MouseEvent e) {
        dealDroppingChess(e);
    }

    private void dealDroppingChess(MouseEvent e) {
        if (!canDropChess) return;
        boolean showDroppingChess = false;
        if (GRID_PANEL.contains(e.getPoint())) {
            for (int row = 0; row < ROW; row++) {
                for (int col = 0; col < COL; col++) {
                    if (!grid[row][col].isDrop() && grid[row][col].getHitbox().getBounds2D().contains(e.getX(), e.getY())) {
                        droppingChess.setPosition(col, row);
                        droppingChess.drop(droppingChess.getType());
                        showDroppingChess = true;
                        break;
                    }
                }
            }
        }
        if (!showDroppingChess) {
            droppingChess.hide();
        }
    }

    public static void main(String[] args) {
        new TicTacToeGame(new RobotChessController()).show();
    }


    public boolean isCanDropChess() {
        return canDropChess;
    }

    public void setCanDropChess(boolean canDropChess) {
        this.canDropChess = canDropChess;
    }

    public void drop(int x, int y, TicTacToeChess.Type type) {
        this.grid[y][x].drop(type);
        this.chessNotes.push(this.grid[y][x]);
    }

    public void drop(int x, int y) {
        this.drop(x, y, this.getUserDropingChessType());
    }


    public boolean checkWin(int x, int y, TicTacToeChess.Type type) {
        // 检查横向
        int count = 1;
        for (int i = x - 1; i >= 0 && grid[y][i].getType() == type; i--) count++;
        for (int i = x + 1; i < 3 && grid[y][i].getType() == type; i++) count++;
        if (count >= 3) return true;
        // 检查纵向
        count = 1;
        for (int i = y - 1; i >= 0 && grid[i][x].getType() == type; i--) count++;
        for (int i = y + 1; i < 3 && grid[i][x].getType() == type; i++) count++;
        if (count >= 3) return true;
        // 检查正斜向
        count = 1;
        for (int i = y - 1, j = x - 1; i >= 0 && j >= 0 && grid[i][j].getType() == type; i--, j--) count++;
        for (int i = y + 1, j = x + 1; i < 3 && j < 3 && grid[i][j].getType() == type; i++, j++) count++;
        if (count >= 3) return true;
        // 检查反斜向
        count = 1;
        for (int i = y - 1, j = x + 1; i >= 0 && j < 3 && grid[i][j].getType() == type; i--, j++) count++;
        for (int i = y + 1, j = x - 1; i < 3 && j >= 0 && grid[i][j].getType() == type; i++, j--) count++;
        return count >= 3;
    }

    public void resetGame() {
        for (int row = 0; row < ROW; row++) {
            for (int col = 0; col < COL; col++) {
                grid[row][col].setType(TicTacToeChess.Type.NONE);
                grid[col][col].hide();
            }
        }

    }

    public void setDropType(TicTacToeChess.Type notUserDropingChessType) {
        this.droppingChess.setType(notUserDropingChessType);
    }

    public TicTacToeChessImpl[][] getGrid() {
        return grid;
    }

    @Override
    public void acceptMessage(ChatMessage chatMessage) {
        messageListJPanel.addMessage(chatMessage);
        SwingUtilities.invokeLater(() -> {
            scrollPane.getViewport().setViewPosition(new Point(0,
                    messageListJPanel.getHeight()));
        });
    }

    public Stack<TicTacToeChessImpl> getChessNotes() {
        return chessNotes;
    }

    public void setDroppingChessType(TicTacToeChess.Type notUserDropingChessType) {
        this.droppingChess.setType(notUserDropingChessType);
    }
}
